﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using System.Threading;
using CID.Domain.Operations;
using CID.Domain.Development;
using CID.Execution.Support.Scripting;
using CID.Execution.Agent.Exceptions;
using CID.Execution.Agent.Tests.Support;

namespace CID.Execution.Agent.Tests
{
    [TestFixture]
    class AgentCoordinatorTests : BaseAgentTests
    {
        private const int SynchronizationWaitTime = 100;
        private static void SynchronizationWait()
        {
            Thread.Sleep(SynchronizationWaitTime);
        }

        private AgentCoordinator agentCoordinator;

        public override void SetUp()
        {
            base.SetUp();
            agentCoordinator = new AgentCoordinator();
            ControllerServiceProxyStub.ClearFlags();
        }

        public override void TearDown()
        {
            if ((agentCoordinator != null) && agentCoordinator.IsStarted)
                agentCoordinator.Stop();

            base.TearDown();
        }

        #region Start
        [Test]
        public void Start_CoordinatorNotRunningYet_ShouldSucceed()
        {
            agentCoordinator.Start();

            Assert.IsTrue(agentCoordinator.IsStarted);
        }

        [Test]
        public void Start_CoordinatorAlreadyRunning_ShouldSucceed()
        {
            agentCoordinator.Start();

            agentCoordinator.Start();

            Assert.IsTrue(agentCoordinator.IsStarted);
        }
        #endregion

        #region Stop
        [Test]
        public void Stop_CoordinatorNotRunningYet_ShouldSucceed()
        {
            agentCoordinator.Stop();

            Assert.IsFalse(agentCoordinator.IsStarted);
        }

        [Test]
        public void Stop_CoordinatorAlreadyRunning_ShouldSucceed()
        {
            agentCoordinator.Start();

            agentCoordinator.Stop();

            Assert.IsFalse(agentCoordinator.IsStarted);
        }
        #endregion

        #region Dispatch(Activation)
        private static LocationActivationScript GetActivationScript()
        {
            var release = GenerateNamedEntityStub<IRelease>();
            var location = GenerateNamedEntityStub<ILocation>();
            
            return new LocationActivationScript(Guid.NewGuid(), release, location, new ActivationScriptStep[0]);
        }

        [Test, ExpectedException(typeof(AgentCoordinatorNotStartedException))]
        public void DispatchActivation_AgentNotStarted_ShouldThrowException()
        {
            agentCoordinator.Dispatch(GetActivationScript());
        }

        [Test]
        public void DispatchActivation_AgentStarted_ShouldSucceed()
        {
            agentCoordinator.Start();

            agentCoordinator.Dispatch(GetActivationScript());
            SynchronizationWait();

            Assert.IsTrue(ControllerServiceProxyStub.NotifyActivationCompletedCalled);
            Assert.IsFalse(ControllerServiceProxyStub.NotifyActivationCompletedWithErrors);
            Assert.IsFalse(ControllerServiceProxyStub.NotifyDeactivationCompletedCalled);
            Assert.IsFalse(ControllerServiceProxyStub.NotifyDeploymentCompletedCalled);
        }

        #endregion

        #region Dispatch(Deactivation)
        private static LocationDeactivationScript GetDeactivationScript()
        {
            var release = GenerateNamedEntityStub<IRelease>();
            var location = GenerateNamedEntityStub<ILocation>();

            return new LocationDeactivationScript(Guid.NewGuid(), release, location, new DeactivationScriptStep[0]);
        }

        [Test, ExpectedException(typeof(AgentCoordinatorNotStartedException))]
        public void DispatchDeactivation_AgentNotStarted_ShouldThrowException()
        {
            agentCoordinator.Dispatch(GetDeactivationScript());
        }

        [Test]
        public void DispatchDeactivation_AgentStarted_ShouldSucceed()
        {
            agentCoordinator.Start();

            agentCoordinator.Dispatch(GetDeactivationScript());
            SynchronizationWait();

            Assert.IsTrue(ControllerServiceProxyStub.NotifyDeactivationCompletedCalled);
            Assert.IsFalse(ControllerServiceProxyStub.NotifyDeactivationCompletedWithErrors);
            Assert.IsFalse(ControllerServiceProxyStub.NotifyActivationCompletedCalled);
            Assert.IsFalse(ControllerServiceProxyStub.NotifyDeploymentCompletedCalled);
        }
        #endregion

        #region Dispatch(Deployment)
        private static LocationDeploymentScript GetDeploymentScript()
        {
            var release = GenerateNamedEntityStub<IRelease>();
            var location = GenerateNamedEntityStub<ILocation>();

            return new LocationDeploymentScript(Guid.NewGuid(), release, location, new DeploymentScriptStep[0]);
        }

        [Test, ExpectedException(typeof(AgentCoordinatorNotStartedException))]
        public void DispatchDeployment_AgentNotStarted_ShouldThrowException()
        {
            agentCoordinator.Dispatch(GetDeploymentScript());
        }

        [Test]
        public void DispatchDeployment_AgentStarted_ShouldSucceed()
        {
            agentCoordinator.Start();

            agentCoordinator.Dispatch(GetDeploymentScript());
            SynchronizationWait();

            Assert.IsTrue(ControllerServiceProxyStub.NotifyDeploymentCompletedCalled);
            Assert.IsFalse(ControllerServiceProxyStub.NotifyDeploymentCompletedWithErrors);
            Assert.IsFalse(ControllerServiceProxyStub.NotifyDeactivationCompletedCalled);
            Assert.IsFalse(ControllerServiceProxyStub.NotifyActivationCompletedCalled);
        }
        #endregion

    }
}
